package com.knight.spring.framework.context;

import com.knight.spring.framework.annotation.KnightAutowired;
import com.knight.spring.framework.annotation.KnightController;
import com.knight.spring.framework.annotation.KnightService;
import com.knight.spring.framework.aop.KnightCglibAopProxy;
import com.knight.spring.framework.aop.KnightJdkDynamicAopProxy;
import com.knight.spring.framework.aop.support.KnightAdvisedSupport;
import com.knight.spring.framework.aop.support.KnightAopConfig;
import com.knight.spring.framework.beans.KnightBeanWrapper;
import com.knight.spring.framework.beans.config.KnightBeanDefinition;
import com.knight.spring.framework.beans.factory.KnightDefaultListableBeanFactory;
import com.knight.spring.framework.beans.factory.support.KnightBeanDefinitionReader;
import com.knight.spring.framework.core.KnightBeanFactory;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class KnightApplicationContext extends KnightDefaultListableBeanFactory implements KnightBeanFactory {

    //单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    //通用的IOC容器
    private Map<String, KnightBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

    private String[] configurationList;
    private KnightBeanDefinitionReader reader;

    public KnightApplicationContext(String... configurationList) {
        this.configurationList = configurationList;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanName) {
        KnightBeanWrapper knightBeanWrapper = factoryBeanInstanceCache.get(beanName);
        if (knightBeanWrapper != null) {
            return knightBeanWrapper.getInstance();
        }
        KnightBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Object instance = instantiateBean(beanName, beanDefinition);
        knightBeanWrapper = new KnightBeanWrapper(instance);

        //设置属性
        populateBean(beanName, beanDefinition, knightBeanWrapper);
        factoryBeanInstanceCache.put(beanName, knightBeanWrapper);

        //创建代理对象
        Object proxyInstance = createProxy(beanName, beanDefinition);
        if (proxyInstance != null) {
            factoryBeanInstanceCache.put(beanName,new KnightBeanWrapper(proxyInstance));
            return proxyInstance;
        }

        return factoryBeanInstanceCache.get(beanName).getInstance();
    }

    private Object createProxy(String beanName, KnightBeanDefinition beanDefinition) {
        KnightAdvisedSupport config = instantionAopConfig();
        config.setTarget(factoryBeanInstanceCache.get(beanName).getInstance());
        try {
            config.setTargetClass(Class.forName(beanDefinition.getBeanClassName()));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (config.pointCutMatch()) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass.getInterfaces().length > 0) {
                return new KnightJdkDynamicAopProxy(config).getProxy();
            }
            return new KnightCglibAopProxy(config);
        }
        return null;
    }

    private KnightAdvisedSupport instantionAopConfig() {
        KnightAopConfig aopConfig = new KnightAopConfig();
        aopConfig.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        aopConfig.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        aopConfig.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        aopConfig.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        aopConfig.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        aopConfig.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));

        return new KnightAdvisedSupport(aopConfig);

    }

    private void populateBean(String beanName, KnightBeanDefinition beanDefinition,
                              KnightBeanWrapper knightBeanWrapper) {
        Object instance = knightBeanWrapper.getInstance();
        Class<?> wrappedClass = knightBeanWrapper.getWrappedClass();

        if (!(wrappedClass.isAnnotationPresent(KnightController.class) || wrappedClass.isAnnotationPresent(KnightService.class))) {
            return;
        }

        Field[] declaredFields = wrappedClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (!declaredField.isAnnotationPresent(KnightAutowired.class)) {
                continue;
            }
            String autowiredBeanName = declaredField.getAnnotation(KnightAutowired.class).value().trim();
            if (autowiredBeanName == null || "".equals(autowiredBeanName)) {
                autowiredBeanName = declaredField.getType().getName();
            }

            //设置强制访问
            declaredField.setAccessible(true);

            if (!factoryBeanInstanceCache.containsKey(autowiredBeanName)) {
                continue;
            }
            try {
                declaredField.set(instance, factoryBeanInstanceCache.get(autowiredBeanName).getInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private Object instantiateBean(String beanName, KnightBeanDefinition beanDefinition) {
        String beanClassName = beanDefinition.getBeanClassName();
        //通过反射创建实例
        Object instance = null;
        try {
            if (singletonObjects.containsKey(beanName)) {
                return singletonObjects.get(beanName);
            } else {
                Class<?> clazz = Class.forName(beanClassName);
                instance = clazz.newInstance();
                singletonObjects.put(beanClassName, instance);
                singletonObjects.put(beanDefinition.getFactoryBeanName(), instance);
            }

        } catch (Exception e) {

        }
        return instance;
    }

    @Override
    public void refresh() throws Exception {
        //1、定位资源文件
        reader = new KnightBeanDefinitionReader(configurationList);
        //2、加载资源文件
        List<KnightBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
        //3、注册把配置文件信息放入到IOC容器中
        doRegisterBeanDefinition(beanDefinitions);
        //4、初始化不是延迟加载的类
        doAutowrited();

    }

    private void doAutowrited() {
        beanDefinitionMap.forEach((k, v) -> {
            if (!v.isLazyInit()) {
                try {
                    getBean(k);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void doRegisterBeanDefinition(List<KnightBeanDefinition> beanDefinitions) {
        for (KnightBeanDefinition beanDefinition : beanDefinitions) {
            if (beanDefinitionMap.containsKey(beanDefinition.getBeanClassName())) {
                throw new RuntimeException("The '" + beanDefinition.getBeanClassName() + "' is exist");
            }
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
            String beanName = beanDefinition.getBeanName();
            if (beanName != null && !"".equals(beanName)) {
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        }


    }

    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }


    public Properties getConfig() {
        return this.reader.getConfig();
    }


}
